package com.kuang.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.kuang.entity.SecurityUser;
import com.kuang.entity.User;
import com.kuang.security.TokenManager;
import com.kuang.util.ResponseUtil;
import com.kuang.util.ResultModel;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;

/**
 * 校验表单中的用户名和密码
 * 认证操作的过滤器,用于匹配URL为"/login"的POST请求做拦截
 * */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;
    private AuthenticationManager authenticationManager;

    public TokenLoginFilter(AuthenticationManager authenticationManager,TokenManager tokenManager, RedisTemplate redisTemplate) {
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
        this.authenticationManager = authenticationManager;
        // 不是只运行post请求,经过这个filter
        this.setPostOnly(false);
        // 设置登录的路径和请求方式
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/user/login","POST"));
    }

    /**
     * 执行认证的方法
     * */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        // 获取表单提供的数据
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            User user = objectMapper.readValue(request.getInputStream(), User.class);
            // 校验 == 认证的过程
            Authentication authenticate = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>())
            );

            return authenticate;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("认证失败");
        }
    }

    /**
     * 认证成功以后调用的方法
     * */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {

        // 得到用户名
        SecurityUser securityUser = (SecurityUser) authResult.getPrincipal();
        String username = securityUser.getUsername();
        // 生成token
        String token = tokenManager.getToken(username);
        // 存入redis 键: 用户名 值:用户权限
        redisTemplate.opsForValue().set(username,securityUser.getPermissionValueList());
        // 返回 token
        ResponseUtil.out(response, ResultModel.success(token));
    }

    /**
     * 认证失败以后调用的方法
     * */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        ResponseUtil.out(response,ResultModel.error(401,failed.getMessage()));
    }
}
